package com.sunmouse.toollib.utils.http;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;

import com.sunmouse.toollib.utils.StringUtils;

import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by leiyun on 2018/5/9.
 */
public class HttpUtil {

    /**
     * "Content-type","application/x-java-serialized-object"
     */
//    public static final MediaType TYPE_MP3 = MediaType.parse("audio/mpeg;charset=utf-8");
//    public static final MediaType TYPE_JPG = MediaType.parse("image/jpeg;charset=utf-8");
//    public static final MediaType TYPE_MP4 = MediaType.parse("video/mpeg;charset=utf-8");

    private final static long CONNECT_TIMEOUT = 10 * 1000;//unit second
    private final static long READ_TIMEOUT = 10 * 1000;//unit second

    public static final int SUCCESS = 0;
    public static final int FAIL = 1;
    public static final int PRE_EXECUTE = 2;

    private HttpURLConnection mConnection;
    private JsonHttpResponse mJsonResponse;

    private Handler mHandler;
    private Message mMsg;

    private String mResult;
    private String mParams;
    private String mFlag;


    private HttpUtil(Builder builder) {
        initHandler();
        this.mParams = builder.getParams();
        this.mFlag = builder.getFlag();
        try {
            URL mUrl = new URL(builder.getUrl());
            this.mConnection = (HttpURLConnection) mUrl.openConnection();
            HashMap<String, String> h = builder.getHeaders();
            for (Map.Entry<String, String> en : h.entrySet()) {
                mConnection.setRequestProperty(en.getKey(), en.getValue());
            }
            long connectTimeOut = builder.getConnectTimeOut();
            long readTimeOut = builder.getReadTimeOut();
            this.mConnection.setConnectTimeout((int) (connectTimeOut < 1 ? CONNECT_TIMEOUT : connectTimeOut));
            this.mConnection.setReadTimeout((int) (readTimeOut < 1 ? READ_TIMEOUT : readTimeOut));

            this.mConnection.setUseCaches(builder.isUseCache());
            this.mConnection.setInstanceFollowRedirects(builder.isFollowRedirects());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * init handler
     */
    private void initHandler() {
        if (mHandler == null) {
//            Looper.prepare();
            mHandler = new ResultHandler(Looper.getMainLooper());
//            Looper.loop();
        }
    }

    /**
     * init
     */
    public static Builder init() {
        return new Builder();
    }


    /**
     * builder
     */
    public static class Builder {
        private StringBuilder mParams;
        private HashMap<String, String> mHeaders;
        private String mFlag;
        private String mUrl;
        private boolean useCache;
        private boolean followRedirects;

        private long readTimeOut;
        private long connectTimeOut;

        public Builder() {
            this.mParams = new StringBuilder();
            this.mHeaders = new HashMap<>();
        }


        public String getFlag() {
            return mFlag;
        }

        public String getUrl() {
            return mUrl;
        }

        public boolean isUseCache() {
            return useCache;
        }

        public boolean isFollowRedirects() {
            return followRedirects;
        }

        public long getReadTimeOut() {
            return readTimeOut;
        }

        public long getConnectTimeOut() {
            return connectTimeOut;
        }

        /**
         * remove "&" at index 0
         *
         * @return
         */
        public String getParams() {
            String p = mParams.toString();
            return TextUtils.isEmpty(p) ? "" : p.substring(1, p.length());
        }

        public HashMap<String, String> getHeaders() {
            return mHeaders;
        }

        /**
         * set mHeaders
         */
        public Builder headers(HashMap<String, String> headers) {
            if (headers != null) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    this.mHeaders.put(entry.getKey(), entry.getValue());
                }
            }
            return this;
        }

        public Builder header(String key, String value) {
            if (!TextUtils.isEmpty(key)) {
                this.mHeaders.put(key, value);
            }
            return this;
        }

        /**
         * connect timeout
         */
        public Builder connectTimeout(int timeout) {
            this.connectTimeOut = timeout * 1000;
            return this;
        }

        /**
         * read  timeout
         */
        public Builder readTimeout(int timeout) {
            this.readTimeOut = timeout;
            return this;
        }

        /**
         * set url
         */
        public Builder url(String url) {
            if (TextUtils.isEmpty(url)) {
                throw new RuntimeException("Error:url is null");
            }
            this.mUrl = url;
            return this;
        }


        /**
         * set params
         */
        public Builder params(HashMap<String, String> params) {
            if (params != null) {
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    mParams.append("&");
                    mParams.append(entry.getKey());
                    mParams.append("=");
                    mParams.append(entry.getValue());
                }
            }
            return this;
        }

        /**
         * set single params
         */
        public Builder param(String key, String value) {
            mParams.append("&");
            mParams.append(key);
            mParams.append("=");
            mParams.append(value);
            return this;
        }

        /**
         * json param
         */
        public Builder jsonParam(String json) {
            mParams.append("&");
            mParams.append(json);
            return this;
        }

        /**
         * json param
         */
        public Builder jsonParam(HashMap<String, String> params) {
            mParams.append("&");
            mParams.append(StringUtils.toJsonString(params));
            return this;
        }

        /**
         * set flag
         */
        public Builder flag(String flag) {
            if (!TextUtils.isEmpty(flag)) {
                this.mFlag = flag;
            }
            return this;
        }

        /**
         * set file
         */
        public Builder audio(String key, File file) {
            if (file != null) {
//            mBuilder.addFormDataPart(key, file.getName(), RequestBody.create(TYPE_MP3, file));
            }
            return this;
        }

        /**
         * set file
         */
        public Builder video(String key, File file) {
            if (file != null) {
//            mBuilder.addFormDataPart(key, file.getName(), RequestBody.create(TYPE_MP4, file));
            }
            return this;
        }

        /**
         * use cathe
         */
        public Builder useCathe(boolean cathe) {
            this.useCache = cathe;
//            this.mConnection.setUseCaches(cathe);
            return this;
        }

        /**
         * redirect
         */
        public Builder instanceFollowRedirects(boolean redirect) {
            this.followRedirects = redirect;
//            this.mConnection.setInstanceFollowRedirects(redirect);
            return this;
        }

        public HttpUtil build(Context context) {
            return new HttpUtil(this);
        }


    }


    /**
     * post
     */
    public synchronized void post(JsonHttpResponse ck) {
        this.mJsonResponse = ck;
        initHandler();
        sendMsg(PRE_EXECUTE, null);
        try {
            this.mConnection.setRequestMethod("POST");
            this.mConnection.setDoOutput(true);
            this.mConnection.setDoInput(true);
            this.mConnection.connect();
            DataOutputStream dos=new DataOutputStream(mConnection.getOutputStream());
            dos.write(mParams.getBytes("utf-8"));

            dos.flush();
            dos.close();
            int status = mConnection.getResponseCode();
            if (status == 200) {
                mResult = StringUtils.toStringFromStream(mConnection.getInputStream());
            }
            JSONObject obj = null;
            if (StringUtils.isJSONObject(mResult)) obj = StringUtils.getJSONObject();
            sendMsg(SUCCESS, obj);
            this.mConnection.disconnect();
        } catch (Exception e) {
            sendMsg(FAIL, e);
            e.printStackTrace();
        }
    }

    public synchronized void post(Serializable p, JsonHttpResponse ck) {
        if (p == null) {
            this.post(ck);
            return;
        }
        this.mJsonResponse = ck;
        initHandler();
        sendMsg(PRE_EXECUTE, null);
        try {
            this.mConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");
            this.mConnection.setRequestMethod("POST");
            this.mConnection.setDoOutput(true);
            this.mConnection.setDoInput(true);
            this.mConnection.connect();
            ObjectOutputStream oos = new ObjectOutputStream(mConnection.getOutputStream());
            oos.writeObject(p);
            oos.flush();
            oos.close();
            int status = mConnection.getResponseCode();
            if (status == 200) {
                mResult = StringUtils.toStringFromStream(mConnection.getInputStream());
            }
            JSONObject obj = null;
            if (StringUtils.isJSONObject(mResult)) obj = StringUtils.getJSONObject();
            sendMsg(SUCCESS, obj);
            this.mConnection.disconnect();
        } catch (Exception e) {
            sendMsg(FAIL, e);
            e.printStackTrace();
        }
    }

    /**
     * combine byte array
     *
     * @param b1
     * @param b2
     * @return
     */
    private byte[] combine(byte[] b1, byte[] b2) {
        byte[] result = new byte[b1.length + b2.length];
        for (int i = 0; i < b1.length; i++) {
            result[i]=b1[i];
        }
        for (int i = 0; i < b2.length; i++) {
            result[b1.length+i]=b2[i];
        }
        return result;
    }


    /**
     * send message
     *
     * @param what
     * @param obj
     */
    private void sendMsg(int what, Object obj) {
        mMsg = Message.obtain();
        mMsg.what = what;
        mMsg.obj = obj;
        mHandler.sendMessage(mMsg);
    }

    /**
     * get
     */
    public synchronized void get(JsonHttpResponse ck) {
        this.mJsonResponse = ck;
        initHandler();
        sendMsg(PRE_EXECUTE, null);
        try {
            this.mConnection.setRequestMethod("GET");
            this.mConnection.setDoInput(true);
            this.mConnection.connect();
            int status = mConnection.getResponseCode();
            if (status == 200) {
                mResult = StringUtils.toStringFromStream(mConnection.getInputStream());
            }
            JSONObject obj = null;
            if (StringUtils.isJSONObject(mResult)) obj = StringUtils.getJSONObject();
            sendMsg(SUCCESS, obj);
            this.mConnection.disconnect();
        } catch (Exception e) {
            sendMsg(FAIL, e);
            e.printStackTrace();
        }
    }

    /**
     * get String
     */
    private String getString(String src) {
        if (TextUtils.isEmpty(src)) return "";
        return src;
    }

    /**
     * from obj to byte
     */
    private byte[] toByte(Object obj) {
        if (obj == null) return null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream ss = new ObjectOutputStream(baos);
            ss.writeObject(obj);
            ss.flush();
            ss.close();
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * mResult handle
     */
    class ResultHandler extends Handler {

        public ResultHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case PRE_EXECUTE:
                    if (mJsonResponse != null) mJsonResponse.onPreExecute(mFlag);
                    break;
                case SUCCESS:
                    if (mJsonResponse != null)
                        mJsonResponse.onSuccess((JSONObject) msg.obj, mFlag);
                    break;
                case FAIL:
                    if (mJsonResponse != null)
                        mJsonResponse.onFailure((Exception) msg.obj, mFlag);
                    break;
            }
        }
    }


}
